'gtklistitemprivate.h',
'gtklistitemfactoryprivate.h',
'gtklistitemmanagerprivate.h',
+ 'gtklistitemwidgetprivate.h',
'gtklockbuttonprivate.h',
'gtkmagnifierprivate.h',
'gtkmediafileprivate.h',
#include "gtkadjustment.h"
#include "gtkintl.h"
+#include "gtklistitemwidgetprivate.h"
#include "gtkorientableprivate.h"
#include "gtkscrollable.h"
#include "gtksingleselection.h"
guint pos;
focus_child = gtk_widget_get_focus_child (GTK_WIDGET (self));
- if (!GTK_IS_LIST_ITEM (focus_child))
+ if (!GTK_IS_LIST_ITEM_WIDGET (focus_child))
return;
- pos = gtk_list_item_get_position (GTK_LIST_ITEM (focus_child));
+ pos = gtk_list_item_widget_get_position (GTK_LIST_ITEM_WIDGET (focus_child));
if (pos != gtk_list_item_tracker_get_position (priv->item_manager, priv->focus))
{
gtk_list_item_tracker_set_position (priv->item_manager,
#endif
#include <gtk/gtkwidget.h>
-#include <gtk/gtklistitem.h>
G_BEGIN_DECLS
#include "gtklistitemprivate.h"
-#include "gtkbinlayout.h"
-#include "gtkcssnodeprivate.h"
-#include "gtkeventcontrollerfocus.h"
-#include "gtkgestureclick.h"
#include "gtkintl.h"
-#include "gtkmain.h"
-#include "gtkwidget.h"
-#include "gtkwidgetprivate.h"
/**
* SECTION:gtklistitem
* The #GtkListItem:item property is not %NULL.
*/
-struct _GtkListItem
-{
- GtkWidget parent_instance;
-
- GObject *item;
- GtkWidget *child;
- guint position;
-
- guint activatable : 1;
- guint selectable : 1;
- guint selected : 1;
-};
-
struct _GtkListItemClass
{
- GtkWidgetClass parent_class;
-
- void (* activate_signal) (GtkListItem *self);
+ GObjectClass parent_class;
};
enum
N_PROPS
};
-enum
-{
- ACTIVATE_SIGNAL,
- LAST_SIGNAL
-};
-
-G_DEFINE_TYPE (GtkListItem, gtk_list_item, GTK_TYPE_WIDGET)
+G_DEFINE_TYPE (GtkListItem, gtk_list_item, G_TYPE_OBJECT)
static GParamSpec *properties[N_PROPS] = { NULL, };
-static guint signals[LAST_SIGNAL] = { 0 };
-
-static void
-gtk_list_item_activate_signal (GtkListItem *self)
-{
- if (!self->activatable)
- return;
-
- gtk_widget_activate_action (GTK_WIDGET (self),
- "list.activate-item",
- "u",
- self->position);
-}
-
-static gboolean
-gtk_list_item_focus (GtkWidget *widget,
- GtkDirectionType direction)
-{
- GtkListItem *self = GTK_LIST_ITEM (widget);
-
- /* The idea of this function is the following:
- * 1. If any child can take focus, do not ever attempt
- * to take focus.
- * 2. Otherwise, if this item is selectable or activatable,
- * allow focusing this widget.
- *
- * This makes sure every item in a list is focusable for
- * activation and selection handling, but no useless widgets
- * get focused and moving focus is as fast as possible.
- */
- if (self->child)
- {
- if (gtk_widget_get_focus_child (widget))
- return FALSE;
- if (gtk_widget_child_focus (self->child, direction))
- return TRUE;
- }
-
- if (gtk_widget_is_focus (widget))
- return FALSE;
-
- if (!gtk_widget_get_can_focus (widget) ||
- !self->selectable)
- return FALSE;
-
- return gtk_widget_grab_focus (widget);
-}
-
-static gboolean
-gtk_list_item_grab_focus (GtkWidget *widget)
-{
- GtkListItem *self = GTK_LIST_ITEM (widget);
-
- if (self->child && gtk_widget_grab_focus (self->child))
- return TRUE;
-
- return GTK_WIDGET_CLASS (gtk_list_item_parent_class)->grab_focus (widget);
-}
static void
gtk_list_item_dispose (GObject *object)
{
GtkListItem *self = GTK_LIST_ITEM (object);
- g_assert (self->item == NULL);
- g_clear_pointer (&self->child, gtk_widget_unparent);
+ g_assert (self->owner == NULL); /* would hold a reference */
+ g_clear_object (&self->child);
G_OBJECT_CLASS (gtk_list_item_parent_class)->dispose (object);
}
}
}
-static void
-gtk_list_item_select_action (GtkWidget *widget,
- const char *action_name,
- GVariant *parameter)
-{
- GtkListItem *self = GTK_LIST_ITEM (widget);
- gboolean modify, extend;
-
- if (!self->selectable)
- return;
-
- g_variant_get (parameter, "(bb)", &modify, &extend);
-
- gtk_widget_activate_action (GTK_WIDGET (self),
- "list.select-item",
- "(ubb)",
- self->position, modify, extend);
-}
-
static void
gtk_list_item_class_init (GtkListItemClass *klass)
{
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- klass->activate_signal = gtk_list_item_activate_signal;
-
- widget_class->focus = gtk_list_item_focus;
- widget_class->grab_focus = gtk_list_item_grab_focus;
-
gobject_class->dispose = gtk_list_item_dispose;
gobject_class->get_property = gtk_list_item_get_property;
gobject_class->set_property = gtk_list_item_set_property;
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
-
- /**
- * GtkListItem::activate-signal:
- *
- * This is a keybinding signal, which will cause this row to be activated.
- *
- * Do not use it, it is an implementation detail.
- *
- * If you want to be notified when the user activates a listitem (by key or not),
- * look at the list widget this item is contained in.
- */
- signals[ACTIVATE_SIGNAL] =
- g_signal_new (I_("activate-keybinding"),
- G_OBJECT_CLASS_TYPE (gobject_class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GtkListItemClass, activate_signal),
- NULL, NULL,
- NULL,
- G_TYPE_NONE, 0);
-
- widget_class->activate_signal = signals[ACTIVATE_SIGNAL];
-
- /**
- * GtkListItem|listitem.select:
- * @modify: %TRUE to toggle the existing selection, %FALSE to select
- * @extend: %TRUE to extend the selection
- *
- * Changes selection if the item is selectable.
- * If the item is not selectable, nothing happens.
- *
- * This function will emit the list.select-item action and the resulting
- * behavior, in particular the interpretation of @modify and @extend
- * depends on the view containing this listitem. See for example
- * GtkListView|list.select-item or GtkGridView|list.select-item.
- */
- gtk_widget_class_install_action (widget_class,
- "listitem.select",
- "(bb)",
- gtk_list_item_select_action);
-
- gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, 0,
- "activate-keybinding", 0);
- gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, 0,
- "activate-keybinding", 0);
- gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter, 0,
- "activate-keybinding", 0);
-
- /* note that some of these may get overwritten by child widgets,
- * such as GtkTreeExpander */
- gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, 0,
- "listitem.select", "(bb)", TRUE, FALSE);
- gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK,
- "listitem.select", "(bb)", TRUE, FALSE);
- gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_SHIFT_MASK,
- "listitem.select", "(bb)", TRUE, FALSE);
- gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
- "listitem.select", "(bb)", TRUE, FALSE);
- gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, 0,
- "listitem.select", "(bb)", TRUE, FALSE);
- gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
- "listitem.select", "(bb)", TRUE, FALSE);
- gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_SHIFT_MASK,
- "listitem.select", "(bb)", TRUE, FALSE);
- gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
- "listitem.select", "(bb)", TRUE, FALSE);
-
- /* This gets overwritten by gtk_list_item_new() but better safe than sorry */
- gtk_widget_class_set_css_name (widget_class, I_("row"));
- gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
-}
-
-static void
-gtk_list_item_click_gesture_pressed (GtkGestureClick *gesture,
- int n_press,
- double x,
- double y,
- GtkListItem *self)
-{
- GtkWidget *widget = GTK_WIDGET (self);
-
- if (!self->selectable && !self->activatable)
- {
- gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
- return;
- }
-
- if (self->selectable)
- {
- GdkModifierType state;
- GdkEvent *event;
- gboolean extend, modify;
-
- event = gtk_gesture_get_last_event (GTK_GESTURE (gesture),
- gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)));
- state = gdk_event_get_modifier_state (event);
- extend = (state & GDK_SHIFT_MASK) != 0;
- modify = (state & GDK_CONTROL_MASK) != 0;
-
- gtk_widget_activate_action (widget,
- "list.select-item",
- "(ubb)",
- self->position, modify, extend);
- }
-
- if (self->activatable)
- {
- if (n_press == 2)
- {
- gtk_widget_activate_action (widget,
- "list.activate-item",
- "u",
- self->position);
- }
- }
-
- gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE);
-
- if (gtk_widget_get_focus_on_click (widget))
- gtk_widget_grab_focus (widget);
-}
-
-static void
-gtk_list_item_enter_cb (GtkEventControllerFocus *controller,
- GtkListItem *self)
-{
- GtkWidget *widget = GTK_WIDGET (self);
-
- gtk_widget_activate_action (widget,
- "list.scroll-to-item",
- "u",
- self->position);
-}
-
-static void
-gtk_list_item_click_gesture_released (GtkGestureClick *gesture,
- int n_press,
- double x,
- double y,
- GtkListItem *self)
-{
- gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
-}
-
-static void
-gtk_list_item_click_gesture_canceled (GtkGestureClick *gesture,
- GdkEventSequence *sequence,
- GtkListItem *self)
-{
- gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
}
static void
gtk_list_item_init (GtkListItem *self)
{
- GtkEventController *controller;
- GtkGesture *gesture;
-
self->selectable = TRUE;
self->activatable = TRUE;
- gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
-
- gesture = gtk_gesture_click_new ();
- gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
- GTK_PHASE_BUBBLE);
- gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture),
- FALSE);
- gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
- GDK_BUTTON_PRIMARY);
- g_signal_connect (gesture, "pressed",
- G_CALLBACK (gtk_list_item_click_gesture_pressed), self);
- g_signal_connect (gesture, "released",
- G_CALLBACK (gtk_list_item_click_gesture_released), self);
- g_signal_connect (gesture, "cancel",
- G_CALLBACK (gtk_list_item_click_gesture_canceled), self);
- gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
-
- controller = gtk_event_controller_focus_new ();
- g_signal_connect (controller, "enter", G_CALLBACK (gtk_list_item_enter_cb), self);
- gtk_widget_add_controller (GTK_WIDGET (self), controller);
}
GtkListItem *
-gtk_list_item_new (const char *css_name)
+gtk_list_item_new (GtkListItemWidget *owner)
{
GtkListItem *result;
- g_return_val_if_fail (css_name != NULL, NULL);
+ g_return_val_if_fail (owner != NULL, NULL);
result = g_object_new (GTK_TYPE_LIST_ITEM,
- "css-name", css_name,
NULL);
+ result->owner = owner;
return result;
}
if (self->child == child)
return;
- g_clear_pointer (&self->child, gtk_widget_unparent);
+ if (self->child && self->owner)
+ gtk_list_item_widget_remove_child (self->owner, self->child);
+
+ g_clear_object (&self->child);
if (child)
{
- gtk_widget_insert_after (child, GTK_WIDGET (self), NULL);
+ g_object_ref_sink (child);
self->child = child;
+ if (self->owner)
+ gtk_list_item_widget_add_child (self->owner, child);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
if (item)
self->item = g_object_ref (item);
- gtk_css_node_invalidate (gtk_widget_get_css_node (GTK_WIDGET (self)), GTK_CSS_CHANGE_ANIMATIONS);
-
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
}
self->selected = selected;
- if (selected)
- gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED, FALSE);
- else
- gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED);
-
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
}
#include "gtklistitemmanagerprivate.h"
-#include "gtklistitemprivate.h"
+#include "gtklistitemwidgetprivate.h"
#include "gtkwidgetprivate.h"
#define GTK_LIST_VIEW_MAX_LIST_ITEMS 200
struct _GtkListItemTracker
{
guint position;
- GtkListItem *widget;
+ GtkListItemWidget *widget;
guint n_before;
guint n_after;
};
if (tracker->widget == NULL)
continue;
- if (g_hash_table_lookup (change, gtk_list_item_get_item (tracker->widget)))
+ if (g_hash_table_lookup (change, gtk_list_item_widget_get_item (tracker->widget)))
break;
}
}
else if (tracker->position >= position)
{
- if (g_hash_table_lookup (change, gtk_list_item_get_item (tracker->widget)))
+ if (g_hash_table_lookup (change, gtk_list_item_widget_get_item (tracker->widget)))
{
/* The item is gone. Guess a good new position */
tracker->position = position + (tracker->position - position) * added / removed;
/* item was put in its right place in the expensive loop above,
* and we updated its position while at it. So grab it from there.
*/
- tracker->position = gtk_list_item_get_position (tracker->widget);
+ tracker->position = gtk_list_item_widget_get_position (tracker->widget);
}
}
else
item = gtk_list_item_manager_get_nth (self, tracker->position, NULL);
g_assert (item != NULL);
g_assert (item->widget);
- tracker->widget = GTK_LIST_ITEM (item->widget);
+ tracker->widget = GTK_LIST_ITEM_WIDGET (item->widget);
}
g_hash_table_iter_init (&iter, change);
item = gtk_list_item_manager_get_nth (self, tracker->position, NULL);
g_assert (item);
- tracker->widget = GTK_LIST_ITEM (item->widget);
+ tracker->widget = GTK_LIST_ITEM_WIDGET (item->widget);
}
}
guint position,
GtkWidget *prev_sibling)
{
- GtkListItem *result;
+ GtkWidget *result;
gpointer item;
gboolean selected;
g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL);
- result = gtk_list_item_new (self->item_css_name);
- if (self->factory)
- gtk_list_item_factory_setup (self->factory, result);
+ result = gtk_list_item_widget_new (self->factory,
+ self->item_css_name);
item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
selected = gtk_selection_model_is_selected (self->model, position);
- if (self->factory)
- gtk_list_item_factory_bind (self->factory, result, position, item, selected);
+ gtk_list_item_widget_bind (GTK_LIST_ITEM_WIDGET (result), position, item, selected);
g_object_unref (item);
- gtk_widget_insert_after (GTK_WIDGET (result), self->widget, prev_sibling);
+ gtk_widget_insert_after (result, self->widget, prev_sibling);
return GTK_WIDGET (result);
}
guint position,
GtkWidget *prev_sibling)
{
- GtkListItem *result;
+ GtkWidget *result;
gpointer item;
g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
if (g_hash_table_steal_extended (change, item, NULL, (gpointer *) &result))
{
- if (self->factory)
- gtk_list_item_factory_update (self->factory, result, position, FALSE);
- gtk_widget_insert_after (GTK_WIDGET (result), self->widget, prev_sibling);
+ gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (result), position, FALSE);
+ gtk_widget_insert_after (result, self->widget, prev_sibling);
/* XXX: Should we let the listview do this? */
- gtk_widget_queue_resize (GTK_WIDGET (result));
+ gtk_widget_queue_resize (result);
}
else
{
}
g_object_unref (item);
- return GTK_WIDGET (result);
+ return result;
}
/**
item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
selected = gtk_selection_model_is_selected (self->model, position);
- if (self->factory)
- gtk_list_item_factory_rebind (self->factory, GTK_LIST_ITEM (list_item), position, item, selected);
+ gtk_list_item_widget_rebind (GTK_LIST_ITEM_WIDGET (list_item), position, item, selected);
gtk_widget_insert_after (list_item, _gtk_widget_get_parent (list_item), prev_sibling);
g_object_unref (item);
}
gboolean selected;
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
- g_return_if_fail (GTK_IS_LIST_ITEM (item));
+ g_return_if_fail (GTK_IS_LIST_ITEM_WIDGET (item));
selected = gtk_selection_model_is_selected (self->model, position);
- if (self->factory)
- gtk_list_item_factory_update (self->factory, GTK_LIST_ITEM (item), position, selected);
+ gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (item), position, selected);
}
/*
GtkWidget *item)
{
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
- g_return_if_fail (GTK_IS_LIST_ITEM (item));
+ g_return_if_fail (GTK_IS_LIST_ITEM_WIDGET (item));
if (change != NULL)
{
- if (g_hash_table_insert (change, gtk_list_item_get_item (GTK_LIST_ITEM (item)), item))
+ if (g_hash_table_insert (change, gtk_list_item_widget_get_item (GTK_LIST_ITEM_WIDGET (item)), item))
return;
g_warning ("FIXME: Handle the same item multiple times in the list.\nLars says this totally should not happen, but here we are.");
}
- if (self->factory)
- {
- gtk_list_item_factory_unbind (self->factory, GTK_LIST_ITEM (item));
- gtk_list_item_factory_teardown (self->factory, GTK_LIST_ITEM (item));
- }
gtk_widget_unparent (item);
}
item = gtk_list_item_manager_get_nth (self, position, NULL);
if (item)
- tracker->widget = GTK_LIST_ITEM (item->widget);
+ tracker->widget = GTK_LIST_ITEM_WIDGET (item->widget);
gtk_widget_queue_resize (self->widget);
}
#include "gtk/gtktypes.h"
-#include "gtk/gtklistitemfactoryprivate.h"
+#include "gtk/gtklistitemfactory.h"
#include "gtk/gtkrbtreeprivate.h"
#include "gtk/gtkselectionmodel.h"
#include "gtklistitem.h"
-#include "gtklistitemmanagerprivate.h"
+#include "gtklistitemwidgetprivate.h"
G_BEGIN_DECLS
-GtkListItem * gtk_list_item_new (const char *css_name);
+struct _GtkListItem
+{
+ GObject parent_instance;
+
+ GtkListItemWidget *owner; /* has a reference */
+
+ GObject *item;
+ GtkWidget *child;
+ guint position;
+
+ guint activatable : 1;
+ guint selectable : 1;
+ guint selected : 1;
+};
+
+GtkListItem * gtk_list_item_new (GtkListItemWidget *owner);
void gtk_list_item_set_item (GtkListItem *self,
gpointer item);
--- /dev/null
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "config.h"
+
+#include "gtklistitemwidgetprivate.h"
+
+#include "gtkbinlayout.h"
+#include "gtkcssnodeprivate.h"
+#include "gtkeventcontrollerfocus.h"
+#include "gtkgestureclick.h"
+#include "gtkintl.h"
+#include "gtklistitemfactoryprivate.h"
+#include "gtklistitemprivate.h"
+#include "gtkmain.h"
+#include "gtkwidget.h"
+#include "gtkwidgetprivate.h"
+
+enum
+{
+ ACTIVATE_SIGNAL,
+ LAST_SIGNAL
+};
+
+G_DEFINE_TYPE (GtkListItemWidget, gtk_list_item_widget, GTK_TYPE_WIDGET)
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+gtk_list_item_widget_activate_signal (GtkListItemWidget *self)
+{
+ if (!self->item->activatable)
+ return;
+
+ gtk_widget_activate_action (GTK_WIDGET (self),
+ "list.activate-item",
+ "u",
+ self->item->position);
+}
+
+static gboolean
+gtk_list_item_widget_focus (GtkWidget *widget,
+ GtkDirectionType direction)
+{
+ GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
+
+ /* The idea of this function is the following:
+ * 1. If any child can take focus, do not ever attempt
+ * to take focus.
+ * 2. Otherwise, if this item is selectable or activatable,
+ * allow focusing this widget.
+ *
+ * This makes sure every item in a list is focusable for
+ * activation and selection handling, but no useless widgets
+ * get focused and moving focus is as fast as possible.
+ */
+ if (self->item && self->item->child)
+ {
+ if (gtk_widget_get_focus_child (widget))
+ return FALSE;
+ if (gtk_widget_child_focus (self->item->child, direction))
+ return TRUE;
+ }
+
+ if (gtk_widget_is_focus (widget))
+ return FALSE;
+
+ if (!gtk_widget_get_can_focus (widget) ||
+ !self->item->selectable)
+ return FALSE;
+
+ return gtk_widget_grab_focus (widget);
+}
+
+static gboolean
+gtk_list_item_widget_grab_focus (GtkWidget *widget)
+{
+ GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
+
+ if (self->item->child && gtk_widget_grab_focus (self->item->child))
+ return TRUE;
+
+ return GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->grab_focus (widget);
+}
+
+static void
+gtk_list_item_widget_dispose (GObject *object)
+{
+ GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (object);
+
+ if (self->item)
+ {
+ if (self->item->item)
+ gtk_list_item_factory_unbind (self->factory, self->item);
+ gtk_list_item_factory_teardown (self->factory, self->item);
+ self->item->owner = NULL;
+ g_clear_object (&self->item);
+ }
+ g_clear_object (&self->factory);
+
+ G_OBJECT_CLASS (gtk_list_item_widget_parent_class)->dispose (object);
+}
+
+static void
+gtk_list_item_widget_select_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *parameter)
+{
+ GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
+ gboolean modify, extend;
+
+ if (!self->item->selectable)
+ return;
+
+ g_variant_get (parameter, "(bb)", &modify, &extend);
+
+ gtk_widget_activate_action (GTK_WIDGET (self),
+ "list.select-item",
+ "(ubb)",
+ self->item->position, modify, extend);
+}
+
+static void
+gtk_list_item_widget_class_init (GtkListItemWidgetClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ klass->activate_signal = gtk_list_item_widget_activate_signal;
+
+ widget_class->focus = gtk_list_item_widget_focus;
+ widget_class->grab_focus = gtk_list_item_widget_grab_focus;
+
+ gobject_class->dispose = gtk_list_item_widget_dispose;
+
+ signals[ACTIVATE_SIGNAL] =
+ g_signal_new (I_("activate-keybinding"),
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GtkListItemWidgetClass, activate_signal),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ widget_class->activate_signal = signals[ACTIVATE_SIGNAL];
+
+ /**
+ * GtkListItem|listitem.select:
+ * @modify: %TRUE to toggle the existing selection, %FALSE to select
+ * @extend: %TRUE to extend the selection
+ *
+ * Changes selection if the item is selectable.
+ * If the item is not selectable, nothing happens.
+ *
+ * This function will emit the list.select-item action and the resulting
+ * behavior, in particular the interpretation of @modify and @extend
+ * depends on the view containing this listitem. See for example
+ * GtkListView|list.select-item or GtkGridView|list.select-item.
+ */
+ gtk_widget_class_install_action (widget_class,
+ "listitem.select",
+ "(bb)",
+ gtk_list_item_widget_select_action);
+
+ gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, 0,
+ "activate-keybinding", 0);
+ gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, 0,
+ "activate-keybinding", 0);
+ gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter, 0,
+ "activate-keybinding", 0);
+
+ /* note that some of these may get overwritten by child widgets,
+ * such as GtkTreeExpander */
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, 0,
+ "listitem.select", "(bb)", TRUE, FALSE);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK,
+ "listitem.select", "(bb)", TRUE, FALSE);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_SHIFT_MASK,
+ "listitem.select", "(bb)", TRUE, FALSE);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+ "listitem.select", "(bb)", TRUE, FALSE);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, 0,
+ "listitem.select", "(bb)", TRUE, FALSE);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
+ "listitem.select", "(bb)", TRUE, FALSE);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_SHIFT_MASK,
+ "listitem.select", "(bb)", TRUE, FALSE);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+ "listitem.select", "(bb)", TRUE, FALSE);
+
+ /* This gets overwritten by gtk_list_item_widget_new() but better safe than sorry */
+ gtk_widget_class_set_css_name (widget_class, I_("row"));
+ gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+}
+
+static void
+gtk_list_item_widget_click_gesture_pressed (GtkGestureClick *gesture,
+ int n_press,
+ double x,
+ double y,
+ GtkListItemWidget *self)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+
+ if (!self->item->selectable && !self->item->activatable)
+ {
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+ return;
+ }
+
+ if (self->item->selectable)
+ {
+ GdkModifierType state;
+ GdkEvent *event;
+ gboolean extend, modify;
+
+ event = gtk_gesture_get_last_event (GTK_GESTURE (gesture),
+ gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)));
+ state = gdk_event_get_modifier_state (event);
+ extend = (state & GDK_SHIFT_MASK) != 0;
+ modify = (state & GDK_CONTROL_MASK) != 0;
+
+ gtk_widget_activate_action (GTK_WIDGET (self),
+ "list.select-item",
+ "(ubb)",
+ self->item->position, modify, extend);
+ }
+
+ if (self->item->activatable)
+ {
+ if (n_press == 2)
+ {
+ gtk_widget_activate_action (GTK_WIDGET (self),
+ "list.activate-item",
+ "u",
+ self->item->position);
+ }
+ }
+
+ gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE);
+
+ if (gtk_widget_get_focus_on_click (widget))
+ gtk_widget_grab_focus (widget);
+}
+
+static void
+gtk_list_item_widget_enter_cb (GtkEventControllerFocus *controller,
+ GtkListItemWidget *self)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+
+ gtk_widget_activate_action (widget,
+ "list.scroll-to-item",
+ "u",
+ self->item->position);
+}
+
+static void
+gtk_list_item_widget_click_gesture_released (GtkGestureClick *gesture,
+ int n_press,
+ double x,
+ double y,
+ GtkListItemWidget *self)
+{
+ gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
+}
+
+static void
+gtk_list_item_widget_click_gesture_canceled (GtkGestureClick *gesture,
+ GdkEventSequence *sequence,
+ GtkListItemWidget *self)
+{
+ gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
+}
+
+static void
+gtk_list_item_widget_init (GtkListItemWidget *self)
+{
+ GtkEventController *controller;
+ GtkGesture *gesture;
+
+ gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
+
+ gesture = gtk_gesture_click_new ();
+ gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
+ GTK_PHASE_BUBBLE);
+ gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture),
+ FALSE);
+ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
+ GDK_BUTTON_PRIMARY);
+ g_signal_connect (gesture, "pressed",
+ G_CALLBACK (gtk_list_item_widget_click_gesture_pressed), self);
+ g_signal_connect (gesture, "released",
+ G_CALLBACK (gtk_list_item_widget_click_gesture_released), self);
+ g_signal_connect (gesture, "cancel",
+ G_CALLBACK (gtk_list_item_widget_click_gesture_canceled), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
+
+ controller = gtk_event_controller_focus_new ();
+ g_signal_connect (controller, "enter", G_CALLBACK (gtk_list_item_widget_enter_cb), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), controller);
+
+ self->item = gtk_list_item_new (self);
+}
+
+GtkWidget *
+gtk_list_item_widget_new (GtkListItemFactory *factory,
+ const char *css_name)
+{
+ GtkListItemWidget *result;
+
+ g_return_val_if_fail (css_name != NULL, NULL);
+
+ result = g_object_new (GTK_TYPE_LIST_ITEM_WIDGET,
+ "css-name", css_name,
+ NULL);
+ if (factory)
+ {
+ result->factory = g_object_ref (factory);
+
+ gtk_list_item_factory_setup (factory, result->item);
+ }
+
+ return GTK_WIDGET (result);
+}
+
+void
+gtk_list_item_widget_bind (GtkListItemWidget *self,
+ guint position,
+ gpointer item,
+ gboolean selected)
+{
+ if (self->factory)
+ gtk_list_item_factory_bind (self->factory, self->item, position, item, selected);
+
+ if (selected)
+ gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED, FALSE);
+ else
+ gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED);
+}
+
+void
+gtk_list_item_widget_rebind (GtkListItemWidget *self,
+ guint position,
+ gpointer item,
+ gboolean selected)
+{
+ if (self->factory)
+ gtk_list_item_factory_rebind (self->factory, self->item, position, item, selected);
+
+ if (selected)
+ gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED, FALSE);
+ else
+ gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED);
+
+ gtk_css_node_invalidate (gtk_widget_get_css_node (GTK_WIDGET (self)), GTK_CSS_CHANGE_ANIMATIONS);
+}
+
+void
+gtk_list_item_widget_update (GtkListItemWidget *self,
+ guint position,
+ gboolean selected)
+{
+ if (self->factory)
+ gtk_list_item_factory_update (self->factory, self->item, position, selected);
+
+ if (selected)
+ gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED, FALSE);
+ else
+ gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED);
+
+}
+
+void
+gtk_list_item_widget_unbind (GtkListItemWidget *self)
+{
+ if (self->factory)
+ gtk_list_item_factory_unbind (self->factory, self->item);
+
+ gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED);
+
+ gtk_css_node_invalidate (gtk_widget_get_css_node (GTK_WIDGET (self)), GTK_CSS_CHANGE_ANIMATIONS);
+}
+
+void
+gtk_list_item_widget_add_child (GtkListItemWidget *self,
+ GtkWidget *child)
+{
+ gtk_widget_set_parent (child, GTK_WIDGET (self));
+}
+
+void
+gtk_list_item_widget_remove_child (GtkListItemWidget *self,
+ GtkWidget *child)
+{
+ gtk_widget_unparent (child);
+}
+
+guint
+gtk_list_item_widget_get_position (GtkListItemWidget *self)
+{
+ return self->item->position;
+}
+
+gpointer
+gtk_list_item_widget_get_item (GtkListItemWidget *self)
+{
+ return self->item->item;
+}
+
--- /dev/null
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#ifndef __GTK_LIST_ITEM_WIDGET_PRIVATE_H__
+#define __GTK_LIST_ITEM_WIDGET_PRIVATE_H__
+
+#include "gtklistitemfactory.h"
+#include "gtkwidget.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_LIST_ITEM_WIDGET (gtk_list_item_widget_get_type ())
+#define GTK_LIST_ITEM_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_WIDGET, GtkListItemWidget))
+#define GTK_LIST_ITEM_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_WIDGET, GtkListItemWidgetClass))
+#define GTK_IS_LIST_ITEM_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_WIDGET))
+#define GTK_IS_LIST_ITEM_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_WIDGET))
+#define GTK_LIST_ITEM_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_WIDGET, GtkListItemWidgetClass))
+
+typedef struct _GtkListItemWidget GtkListItemWidget;
+typedef struct _GtkListItemWidgetClass GtkListItemWidgetClass;
+
+struct _GtkListItemWidget
+{
+ GtkWidget parent_instance;
+
+ GtkListItemFactory *factory;
+ GtkListItem *item;
+};
+
+struct _GtkListItemWidgetClass
+{
+ GtkWidgetClass parent_class;
+
+ void (* activate_signal) (GtkListItemWidget *self);
+};
+
+GType gtk_list_item_widget_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gtk_list_item_widget_new (GtkListItemFactory *factory,
+ const char *css_name);
+
+void gtk_list_item_widget_bind (GtkListItemWidget *self,
+ guint position,
+ gpointer item,
+ gboolean selected);
+void gtk_list_item_widget_rebind (GtkListItemWidget *self,
+ guint position,
+ gpointer item,
+ gboolean selected);
+void gtk_list_item_widget_update (GtkListItemWidget *self,
+ guint position,
+ gboolean selected);
+void gtk_list_item_widget_unbind (GtkListItemWidget *self);
+
+void gtk_list_item_widget_add_child (GtkListItemWidget *self,
+ GtkWidget *child);
+void gtk_list_item_widget_remove_child (GtkListItemWidget *self,
+ GtkWidget *child);
+
+guint gtk_list_item_widget_get_position (GtkListItemWidget *self);
+gpointer gtk_list_item_widget_get_item (GtkListItemWidget *self);
+
+G_END_DECLS
+
+#endif /* __GTK_LIST_ITEM_WIDGET_PRIVATE_H__ */
'gtklistitem.c',
'gtklistitemfactory.c',
'gtklistitemmanager.c',
+ 'gtklistitemwidget.c',
'gtklistlistmodel.c',
'gtkliststore.c',
'gtklistview.c',
RowData *data;
data = g_slice_new0 (RowData);
- g_signal_connect (list_item, "notify::item", G_CALLBACK (row_data_notify_item), data);
- g_object_set_data_full (G_OBJECT (list_item), "row-data", data, row_data_free);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_list_item_set_child (list_item, box);
gtk_label_set_max_width_chars (GTK_LABEL (data->name), 25);
gtk_label_set_ellipsize (GTK_LABEL (data->name), PANGO_ELLIPSIZE_END);
gtk_box_append (GTK_BOX (box), data->name);
+
+ g_signal_connect (list_item, "notify::item", G_CALLBACK (row_data_notify_item), data);
+ g_object_set_data_full (G_OBJECT (list_item), "row-data", data, row_data_free);
}
static GListModel *